package com.seari.ats.handler;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;

import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.TimeUnit;

import org.slf4j.LoggerFactory;

import redis.clients.jedis.Client;
import redis.clients.jedis.Jedis;

import com.seari.bean.ats.CiosAtsBasInfo;
import com.seari.bean.ats.CiosAtsFasInfo;
import com.seari.bean.ats.CiosAtsHelper;
import com.seari.bean.ats.CiosAtsScadaInfo;
import com.seari.commonutils.redis.JedisUtilProxy;
import com.seari.utils.DataUtil;
import com.seari.utils.GuavaCacheUtil;
import com.seari.utils.PropertiesUtil;

public class AtsSender  {
	// 日志
	protected static org.slf4j.Logger logger = LoggerFactory
			.getLogger(AtsSender.class);

	// Fas List
	private static List<CiosAtsFasInfo> fasList = null;
	// Scada List
	private static List<CiosAtsScadaInfo> scadaList = null;
	// Bas List
	private static List<CiosAtsBasInfo> basList = null;

	// guava list
	private static List<CiosAtsFasInfo> guavaFas = null;
	private static List<CiosAtsScadaInfo> guavaScada = null;
	private static List<CiosAtsBasInfo> guavaBas = null;

	static PropertiesUtil prop = null;

	static List<Object> BasIdList = null;

	static Properties properties = null;

	static {
		try {
			// 创建文件解析
			prop = new PropertiesUtil();
			prop.load(new InputStreamReader(Client.class.getClassLoader()
					.getResourceAsStream("sendBas.properties"), "UTF-8"));
			properties = PropertiesUtil.loadConfig("redisAddress.properties");

			// 启动时加载配置文件读取配置信息
			BasIdList = prop.getKeyList();
		} catch (Exception e) {
			logger.info("Failed to load configuration file !");
		}
	}

	Channel channel;

	public AtsSender(Channel channel) {
		this.channel = channel;
	}

	public void CiosSendData() {

		String address = properties.getProperty("atsAddress");
		int port = Integer.parseInt(properties.getProperty("atsPort"));

		// 初始化连接池
		JedisUtilProxy.initJedisPoolUtil(address, port);

		// 获取线程
		channel.eventLoop().scheduleAtFixedRate(new Runnable() {
			@SuppressWarnings("unchecked")
			@Override
			public void run() {
				Jedis fasJedis = null;
				try {
					fasList = new ArrayList<CiosAtsFasInfo>();
					fasJedis = JedisUtilProxy.getJedis();
					for (byte i = 0; i <= 31; i++) {
						CiosAtsFasInfo fasinfo = new CiosAtsFasInfo();
						String statu = fasJedis.hget("SCADA_DATA",
								String.valueOf(i));
						fasinfo.setFasId((byte) (i + 1));
						switch (statu.getBytes()[0]) {
						case 48:
							// 48 对应 0 , modbus 0 对应无火警 , 无火警Ats 对应 2 .
							fasinfo.setStatus(CiosAtsHelper.Down);
							break;
						case 49:
							// 49 对应 1 , modbus 1 对应火警 , ATs火警 为 1
							fasinfo.setStatus(CiosAtsHelper.Fire);
							break;
						default:
							// 其他情况 , ATS 未知 为 0
							fasinfo.setStatus(CiosAtsHelper.UnKnown);
							break;
						}
						// 添加对象
						fasList.add(fasinfo);
					}
				} catch (Exception e) {
					
					logger.info("jedis close fail ...  fasList");

				} finally {
					JedisUtilProxy.close(fasJedis);
				}

				// 从guava中取出basList 并判断是否相同 相同不做操作
				guavaFas = (List<CiosAtsFasInfo>) GuavaCacheUtil.getIfPresent("FAS");
				if (!isListEqual(guavaFas, fasList)) {
					// 如果不相同 , 缓存新的FasList 并调用send方法
					GuavaCacheUtil.put("FAS", fasList);
					sendFasInfo(fasList);
				}
				Jedis ScadaJedis = null;
				try {
					ScadaJedis = JedisUtilProxy.getJedis();
					// SCADA
					scadaList = new ArrayList<CiosAtsScadaInfo>();
					byte count = 1;

					for (byte i = 32; i <= 65; i++) {
						CiosAtsScadaInfo scadaInfo = new CiosAtsScadaInfo();
						String statu = ScadaJedis.hget("SCADA_DATA",
								String.valueOf(i));
						byte[] scadaId = new byte[2];
						scadaId[0] = 0x00;
						scadaId[1] = count;
						scadaInfo.setScadaId(scadaId);
						count += 1;
						switch (statu.getBytes()[0]) {
						case 48:
							// 48 对应 0 , modbus 0 对应无电 , 无电Ats 对应 2 .
							scadaInfo.setStatus(CiosAtsHelper.Down);
							break;
						case 49:
							// 49 对应 1 , modbus 1 对应有电 , ATs有电 为 1
							scadaInfo.setStatus(CiosAtsHelper.Fire);
							break;
						default:
							// 其他情况 , ATS 未知 为 0
							scadaInfo.setStatus(CiosAtsHelper.UnKnown);
							break;
						}
						scadaList.add(scadaInfo);

					}
				} catch (Exception e) {
					
					logger.info("jedis close fail ...  scadaList");

				} finally {
					JedisUtilProxy.close(ScadaJedis);
				}

				// 从guava中取出basList 并判断是否相同 相同不做操作
				guavaScada = (List<CiosAtsScadaInfo>) GuavaCacheUtil
						.getIfPresent("SCADA");
				if (!isListEqual(guavaScada, scadaList)) {
					// 如果不相同 , 缓存新的ScadaList 并调用send方法
					GuavaCacheUtil.put("SCADA", scadaList);
					sendScadaInfo(scadaList);
				}
				Jedis BasJedis = null;
				try {
					BasJedis = JedisUtilProxy.getJedis();
					// BAS
					basList = new ArrayList<CiosAtsBasInfo>();

					String statu = null; //
					int index = 0; // 下标
					CiosAtsBasInfo basInfo = null;
					for (int i = 66; i <= 683; i++) {
						basInfo = new CiosAtsBasInfo();
						// i = 64
						statu = BasJedis.hget("SCADA_DATA", String.valueOf(i));

						// bas_id 两个字节
						basInfo.setBasId(DataUtil.getBasId(BasIdList, index));

						switch (statu.getBytes()[0]) {
						case 48:
							// 48 对应 0 , modbus 0 对应未知 未知跳出判断 执行下一判断
							i += 1; // i = 65

							statu = BasJedis.hget("SCADA_DATA", String.valueOf(i));
							switch (statu.getBytes()[0]) {
							case 48:
								// 48 对应 0 , modbus 0 对应未知 1 对应正转
								basInfo.setDirection(CiosAtsHelper.UnKnown);
								i += 1;
								break;
							case 49:
								// 49 对应 1 , modbus 1 对应反转 , Ats 2 为反转
								basInfo.setDirection(CiosAtsHelper.Down);
								i += 1;
								break;
							default:
								// 其他情况
								basInfo.setDirection(CiosAtsHelper.UnKnown);
								i += 1;
								break;
							}
							break;
						case 49:
							// 49 对应 1 , modbus 1 对应正转 , Ats 1 为正转
							basInfo.setDirection(CiosAtsHelper.Fire);
							// 如果为正转 , 直接+2 判断statu
							i += 2;
							break;
						default:
							// 其他情况
							basInfo.setDirection(CiosAtsHelper.UnKnown);
							i += 2;
							break;
						}

						statu = BasJedis.hget("SCADA_DATA", String.valueOf(i));
						switch (statu.getBytes()[0]) {
						case 48:
							// 48 对应 0 , modbus 0 对应未知 1对应开启 2 对应关闭
							// 当正转反转信息不为0(未知)时 , 风机判定为开启状态
							if (basInfo.getDirection() != 0) {
								basInfo.setStatu(CiosAtsHelper.Fire);
							} else {
								basInfo.setStatu(CiosAtsHelper.UnKnown);
							}
							break;
						case 49:
							// 49 对应 1 , modbus 1 对应停止 , ATS 2 对应 off
							basInfo.setStatu(CiosAtsHelper.Down);
							break;
						default:
							// 48 对应 0 , modbus 0 对应未知 1对应开启 2 对应关闭
							if (basInfo.getDirection() != 0) {
								basInfo.setStatu(CiosAtsHelper.Fire);
							} else {
								basInfo.setStatu(CiosAtsHelper.UnKnown);
							}
							break;
						}
						index += 1;
						basList.add(basInfo);
					}
				} catch (Exception e) {
					
					logger.info("jedis close fail ...  basList");

				} finally {
					JedisUtilProxy.close(BasJedis);
				}

				// 从guava中取出basList 并判断是否相同 相同不做操作
				guavaBas = (List<CiosAtsBasInfo>) GuavaCacheUtil
						.getIfPresent("BAS");
				if (!isListEqual(guavaBas, basList)) {
					// 如果不相同 , 缓存新的BASList 并调用send方法
					GuavaCacheUtil.put("BAS", basList);
					sendBasInfo(basList);
				}

			}
		}, 3, 3, TimeUnit.SECONDS);
	}

	// 发送火灾信息 (实际)
	public void sendFasInfo(List<CiosAtsFasInfo> list) {

		ByteBuf byteBuf = null;
		byte[] bytes = new byte[list.size() * 2];
		byte count = 0;
		for (int i = 0; i < list.size(); i++) {

			bytes[count] = list.get(i).getFasId();

			count += 1;

			bytes[count] = list.get(i).getStatus();

			count += 1;
		}
		byteBuf = channel.alloc().buffer(count + 9);
		// SystemID
		byteBuf.writeByte(CiosAtsHelper.SystemID);

		// Total Length
		byteBuf.writeByte(0x00);
		byteBuf.writeByte(count + 6); // count + 1(Multi-flag) + 2(Lenth) +
										// 2(Msg_id) + 1 (Number_of_stations)

		// Multi-flag
		byteBuf.writeByte(CiosAtsHelper.MultiFlag);

		// Lenth
		byteBuf.writeByte(0x00);
		byteBuf.writeByte(count + 3); // count = 60 = 30 车站

		// Msg_id
		byteBuf.writeBytes(CiosAtsHelper.FasMsgId);

		// Number_of_stations
		byteBuf.writeByte(count / 2);

		byteBuf.writeBytes(bytes);
		channel.writeAndFlush(byteBuf);
	}

	// 发送牵引分段信息 (实际)
	public void sendScadaInfo(List<CiosAtsScadaInfo> list) {
		ByteBuf byteBuf = null;
		byte[] bytes = new byte[list.size() * 3];
		byte count = 0;
		for (int i = 0; i < list.size(); i++) {
			byte[] scadaId = list.get(i).getScadaId();
			for (int j = 0; j < scadaId.length; j++) {
				bytes[count] = scadaId[j];
				count += 1;
			}

			bytes[count] = list.get(i).getStatus();
			count += 1;

		}
		byteBuf = channel.alloc().buffer(count + 10);
		// SystemID 1
		byteBuf.writeByte(CiosAtsHelper.SystemID);

		// Total Length 2
		byteBuf.writeByte(0x00);
		byteBuf.writeByte(count + 7); // count + 1(Multi-flag) + 2(Lenth) +
										// 2(Msg_id) + 1 (Number_of_stations)

		// Multi-flag 1
		byteBuf.writeByte(CiosAtsHelper.MultiFlag);

		// Lenth
		byteBuf.writeByte(0x00);
		byteBuf.writeByte(count + 4); // count = 60 = 30 车站

		// Msg_id
		byteBuf.writeBytes(CiosAtsHelper.ScadaMsgId);

		// Section number 2 baty
		byteBuf.writeByte(0x00);
		byteBuf.writeByte(count / 3);

		byteBuf.writeBytes(bytes);
		channel.writeAndFlush(byteBuf);

	}

	// 发送隧道风机信息(实际)
	public void sendBasInfo(List<CiosAtsBasInfo> list) {

		ByteBuf byteBuf = null;
		byte[] bytes = new byte[list.size() * 4];
		int count = 0;
		for (int i = 0; i < list.size(); i++) {

			byte[] basId = list.get(i).getBasId();
			for (int j = 0; j < basId.length; j++) {
				bytes[count] = basId[j];

				count += 1;
			}
			bytes[count] = list.get(i).getStatu();
			count += 1;
			bytes[count] = list.get(i).getDirection();
			count += 1;
		}
		System.out.println("count.........." + count);
		byteBuf = channel.alloc().buffer(count + 10);
		// SystemID
		byteBuf.writeByte(CiosAtsHelper.SystemID);

		// Total Length
		byte[] totalLength = DataUtil.short2byte(count + 7);
		byteBuf.writeBytes(totalLength);

		// Multi-flag
		byteBuf.writeByte(CiosAtsHelper.MultiFlag);

		// Lenth
		byte[] length = DataUtil.short2byte(count + 4);
		byteBuf.writeBytes(length);

		// Msg_id
		byteBuf.writeBytes(CiosAtsHelper.BasMsgId);

		// Number_of_stations
		byteBuf.writeByte(0x00);
		byteBuf.writeByte(count / 4);

		byteBuf.writeBytes(bytes);
		channel.writeAndFlush(byteBuf);

	}

	// 比较两个集合是否相同
	/**
	 * 首先进行入参检查防止出现空指针异常 如果两个参数都为空，则返回true 如果有一项为空，则返回false
	 * 接着对第一个list进行遍历，如果某一项第二个list里面没有，则返回false
	 * 还要再将两个list反过来比较，因为可能一个list是两一个list的子集 如果成功遍历结束，返回true
	 * 
	 * @param l0
	 * @param l1
	 * @return
	 */
	public static boolean isListEqual(List l0, List l1) {
		if (l0 == l1)
			return true;
		if (l0 == null && l1 == null)
			return true;
		if (l0 == null || l1 == null)
			return false;
		if (l0.size() != l1.size())
			return false;
		for (Object o : l0) {
			if (!l1.contains(o))
				return false;
		}
		for (Object o : l1) {
			if (!l0.contains(o))
				return false;
		}
		return true;
	}

}
